#!/usr/bin/env ruby

require 'socket'
require 'rubygems'
require 'base64'

gem 'base32'
require 'base32'

gem 'pNet-DNS'
require 'Net/DNS'

PORT=53
BIND_ADDR="127.0.0.1"
MAX_PACKET_SIZE=8192

DESTINATION_SOCKET_BIND_ADDR="127.0.0.1"
DESTINATION_SOCKET_LOCAL_PORT="59000"
DESTINATION_SOCKET_REMOTE_ADDR="127.0.0.1"
DESTINATION_SOCKET_REMOTE_PORT=4001

queue = Queue.new

destination_socket = UDPSocket.new
destination_socket.bind DESTINATION_SOCKET_BIND_ADDR, DESTINATION_SOCKET_LOCAL_PORT

# read data from destination host, putting it on the queue
Thread.new do
  while true
    queue << destination_socket.recvfrom(MAX_PACKET_SIZE)[0]
    puts "added packet to queue"
  end
end

# receive requests from remove clients, sending the data to the destination host
# and sending back any data on the queue
Thread.new do
  dns_socket = UDPSocket.new
  dns_socket.bind BIND_ADDR, PORT

  while true
    packet = dns_socket.recvfrom MAX_PACKET_SIZE
    puts "received dns request"

    request = Net::DNS::Packet.new_from_binary packet[0]
    encoded_payload = request.question[0].zname
    bits = encoded_payload.split '.'
    type = bits[1]

    if type == "data"
      payload = Base32.decode bits[0]
      # send the data to the destination. responses will be handled by the other thread
      destination_socket.send Base32.decode(encoded_payload.split('.')[0]), 0, DESTINATION_SOCKET_REMOTE_ADDR, DESTINATION_SOCKET_REMOTE_PORT
    end

    # respond to the DNS request with any data queued up
    response = Net::DNS::Packet.new
    response.header = Net::DNS::Header.new
    response.header.id, response.header.opcode = request.header.id, request.header.opcode
    response.header.qr = response.header.aa = response.header.qdcount = response.header.ancount = 1
    response.question = request.question

    begin
      data = Base64.encode64 queue.pop(true)
      response.answer = [Net::DNS::RR.create("%s 0 IN TXT \"%s\"" % [encoded_payload, data])]
      puts "sending %i bytes of data with dns response" % data.length
    rescue ThreadError
      puts "no data to send with dns response"
    end

    response.authority = response.additional = []

    dns_socket.send response.data, 0, packet[1][3], packet[1][1]
    puts "sent dns response"
  end
end

sleep 60 while true
